#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/epoll.h>
#define SERPORT 8001

int main(int argc, char* argv[]){
    //创建一个套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd < 0){
        perror("socket error");
        exit(1);
    }
    //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    struct sockaddr_in seraddr, cliaddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SERPORT);
    //192.168.0.184  主机字节序的字符串
    seraddr.sin_addr.s_addr = INADDR_ANY;//网络字节序的整形 INADDR_ANY = 0
    //给套接字绑定了IP+port
    int bret = bind(lfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if(bret < 0){
        perror("bind error");
        exit(1);
    }
    int lret = listen(lfd, 64);
    printf("listen is return\n");
    //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    socklen_t addrlen = sizeof(cliaddr);
    //从全连接队列取出一个连接，返回的值是和客户端通信的socket的文件描述符
    //我们希望epoll帮我门监听哪个文件描述符的哪个事件  lfd的读事件
    
    int epfd = epoll_create(64);
    //让epoll帮助我们监听lfd
    struct epoll_event event;
    struct epoll_event events[1024];
    event.events = EPOLLIN;
    event.data.fd = lfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &event);
    char buf[1024];
    while(1){
        //epoll帮助我们阻塞监听,epoll的返回值：有多少个监听的文件描述符触发了
        int wret = epoll_wait(epfd, events, 1024, -1);
        if(wret < 0){
            perror("epoll_wait error");
            exit(1);
        }
        //epoll_wait返回的时候，我们需要寻找哪些事件触发了，然后把他处理掉
        //两类文件描述符：lfd, cfd
        for(int i = 0; i < wret; i++){
            if(events[i].events & EPOLLIN){
                if( events[i].data.fd == lfd){
                    int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &addrlen);
                    if(cfd < 0){
                        perror("accept error");
                        exit(1);
                    }
                    printf("新的客户端建立连接\n");
                    //新增加的cfd也需要交给epoll监听
                    event.events = EPOLLIN;
                    event.data.fd = cfd;
                    epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);
                }
                else{
                    int rret = read(events[i].data.fd, buf, sizeof(buf));
                    if(rret == -1){
                        perror("read error");
                        exit(1);
                    }
                    else if(rret == 0){
                        printf("客户端断开连接\n");
                        epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                        close(events[i].data.fd);
                    }
                    else{
                        write(events[i].data.fd, buf, rret);
                        write(1, buf, rret);
                    }
                }
            }
        }
    }
    return 0;
}